﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using CommonLibrary;
using CommonLibrary.Extensions;
using CommonLibrary.Models.ControllerStates.PS3;
using InputManager.Helpers;
using InputManager.Models;

namespace InputManager.AdvOperation
{
    public class CalibrationTools
    {
        private enum CircleType
        {
            Regular,
            DeadZone
        }
        private MouseTranslatorCalculator _mouseCalculator;

        private double _angle; //between 0 and 2 * PI, angle is in radians

        private Thread _localInputThreads;
        private static readonly ManualResetEvent ShutdownEvent = new ManualResetEvent(false);

        private double _autoIncrement;
        private int _fixedOffset = 5;
        private CircleType _circleType = CircleType.Regular;

        private AxisOffset _position;

        #region Constructor
        private readonly IAppContext _appContext;
        
        public CalibrationTools(IAppContext context)
        {
            _appContext = context;
            _mouseCalculator = new MouseTranslatorCalculator();
        }
        #endregion

        #region Public Methods
        public void CalibrationCircle()
        {
            //if (_localInputThreads != null && _localInputThreads.IsAlive)
            //{
            //    ShutdownEvent.Set();
            //    _localInputThreads.Join();
            //    _localInputThreads = null;
            //    return;
            //}
            if (StopCalibrationCircle()) return;

            _localInputThreads = new Thread(CircleThread);
            ShutdownEvent.Reset();
            _localInputThreads.Start();
            _angle = 0;
        }

        public bool StopCalibrationCircle()
        {
            if (_localInputThreads == null || !_localInputThreads.IsAlive) return false;

            ShutdownEvent.Set();
            _localInputThreads.Join();
            _localInputThreads = null;
            return true;
        }

        public void ChangeCircleType(string type)
        {
            switch (type)
            {
                case "Regular":
                    _circleType = CircleType.Regular;
                    break;
                case "DeadZone":
                    _circleType = CircleType.DeadZone;
                    break;
            }
        }

        #region Auto handle circle diameter
        public void AutoIncreaseCircle()
        {
            if (_circleType == CircleType.DeadZone)
                _autoIncrement = _autoIncrement == 0.5 ? 0 : 0.5;
            else
                _autoIncrement = 0;
        }

        public void AutoDecreaseCircle()
        {
            if (_circleType == CircleType.DeadZone)
                _autoIncrement = _autoIncrement == -0.5 ? 0 : -0.5;
            else
                _autoIncrement = 0;
        }
        #endregion

        #region Auto set circle diameter
        public void IncreaseCircle()
        {
            _autoIncrement = 0;
            if (_circleType == CircleType.DeadZone)
            {
                IncrementDeadZoneX(0.1);
                IncrementDeadZoneY(0.1);
            }
            else if (_circleType == CircleType.Regular)
            {
                _fixedOffset += 1;
            }
        }

        public void DecreaseCircle()
        {
            _autoIncrement = 0;
            if (_circleType == CircleType.DeadZone)
            {
                IncrementDeadZoneX(-0.1);
                IncrementDeadZoneY(-0.1);
            }
            else if (_circleType == CircleType.Regular)
            {
                _fixedOffset += -1;
            }
        }
        #endregion
        #endregion

        private void CircleThread()
        {
            while (true)
            {
                if (ShutdownEvent.WaitOne(0))
                {
                    _mouseCalculator.ConfigMouseMovement(_angle, 0, 0, 0, 0);
                    break;
                }

                #region Make a circle
                if (_angle >= 2 * Math.PI)
                    _angle = 0;
                else
                    _angle += 0.025;

                _angle = _angle.Clamp(0, 2 * Math.PI);
                #endregion

                #region Auto handle circle diameter
                IncrementDeadZoneX(_autoIncrement);
                IncrementDeadZoneY(_autoIncrement);
                #endregion

                #region Calculate circle
                _position = _mouseCalculator.ConfigMouseMovement(_angle, GetDeadZoneX(), GetMultiplierX(), GetDeadZoneY(), GetMultiplierY());
                #endregion

                #region Send data to DataStructure                

                // TODO Abstract the structure handler
                (_appContext.ControllerState as PS3ControllerState).MouseMovementToPS3CS("RightX", _position.X);
                (_appContext.ControllerState as PS3ControllerState).MouseMovementToPS3CS("RightY", _position.Y);
                #endregion

                Thread.Sleep(10); // Light load
            }
        }

        #region Helpers
        // TODO Hard coded to the right stick (aiming) stick, fix that
        private double GetDeadZoneY()
        {
            if (_circleType == CircleType.Regular)
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_Y").Value.DeadZone + _fixedOffset;
            else
                return 0;
        }

        private double GetDeadZoneX()
        {
            if (_circleType == CircleType.Regular)
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_X").Value.DeadZone + _fixedOffset;
            else
                return 0;
        }

        private double GetMultiplierY()
        {
            if(_circleType == CircleType.Regular)
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_Y").Value.Multiplier;
            else
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_Y").Value.DeadZone;
        }

        private double GetMultiplierX()
        {
            if (_circleType == CircleType.Regular)
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_X").Value.Multiplier;
            else // if we need to find the dead zone the multiplier becomes the DeadZone
                return _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_X").Value.DeadZone;
        }

        private void IncrementDeadZoneY(double value)
        {
            _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_Y").Value.DeadZone += value;
        }

        private void IncrementDeadZoneX(double value)
        {
            _appContext.ProfileConfig.AxisAction.FirstOrDefault(p => p.Key == "Mouse_Axis_X").Value.DeadZone += value;
        }
        #endregion

        #region Other
        [Obsolete("Dont use .ToString()", true)]
        public override string ToString()
        {
            return base.ToString();
        }

        [Obsolete("Dont use .Equals(object obj)", true)]
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        [Obsolete("Dont use .GetHashCode()", true)]
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        #endregion
    }
}
